#include "stdio.h"
#include "interrupt.h"
#include "global.h"
#include "string.h"
#include "syscall.h"
#include "print.h"

#define va_start(ap, v) ap = (va_list)&v  // 把ap指向第一个固定参数v
#define va_arg(ap, t) *((t*)(ap += 4))	  // ap指向下一个参数并返回其值
#define va_end(ap) ap = NULL		      // 清除ap

// 将整形转换成字符
static void itoa(uint32_t value, char** buf_ptr_addr, uint8_t base) {
    uint32_t m = value % base; // 取模
    uint32_t i = value / base; // 取整
    if(i) itoa(i, buf_ptr_addr, base);          // 若为0表示已经除到头了、转换完了
    if(m < 10) *((*buf_ptr_addr)++) = m + '0';  // 若取模得到的余数 < 10，则转换为 '0'~'9' 字符
    else *((*buf_ptr_addr)++) = m - 10 + 'A';   // 若取模得到的余数 >= 10，则转换为 'A'~'F' 字符
}

// 将参数 ap 按照格式 format 输出到字符串 str，并返回替换后的 str 长度
uint32_t vsprintf(char* str, const char* format, va_list ap) {
    char* buf_ptr = str;
    const char* index_ptr = format;
    char index_char = *index_ptr; // 指向 format 字符串某个字符
    int32_t arg_int;
    char* arg_str;
    while(index_char) {
        if(index_char != '%') { // 处理当前字符不是 % 的情况
            *(buf_ptr++) = index_char;  // 先把字符输出到 str，然后++
            index_char = *(++index_ptr);// 得到 format 下一个字符，赋值给 index_char
            continue;
        }
        index_char = *(++index_ptr); // 得到 % 后的字符，它表示数据类型
        switch(index_char) {
            case 's':
                arg_str = va_arg(ap, char*);
                strcpy(buf_ptr, arg_str);
                buf_ptr += strlen(arg_str);
                index_char = *(++index_ptr);
                break;
            case 'c':
                *(buf_ptr++) = va_arg(ap, char);
                index_char = *(++index_ptr);
                break;
            case 'd':
                arg_int = va_arg(ap, int);
                if(arg_int < 0) { // 若为负数，将其转为正数后，在前面添加一个负号'-'
                    arg_int = 0 - arg_int;
                    *buf_ptr++ = '-';
                }
                itoa(arg_int, &buf_ptr, 10);
                index_char = *(++index_ptr);
                break;
            case 'x':
                arg_int = va_arg(ap, int);
                itoa(arg_int, &buf_ptr, 16);
                index_char = *(++index_ptr); // 指向 format 的下一个字符
                break;
        }
    }
    return strlen(str);
}

/* 同printf不同的地方就是字符串不是写到终端,而是写到buf中 */
uint32_t sprintf(char* buf, const char* format, ...) {
    va_list args;
    uint32_t retval;
    va_start(args, format);
    retval = vsprintf(buf, format, args);
    va_end(args);
    return retval;
}

// 格式化输出字符串 format
uint32_t printf(const char* format, ...) {
    va_list args;
    va_start(args, format);     // 将 args 指向 format
    char buf[1024] = {0};       // 用于存储拼接后的字符串
    vsprintf(buf, format, args);
    va_end(args);
    return write(buf);
}
